<?php

namespace App\Repository\XhApi;

use App\Models\Orders\OrdersModel;
use App\Models\Orders\OrdersUserModel;
use App\Models\Xh\MemberModel;
use App\Models\Xh\PaysModel;
use App\Repository\BaseRepository;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

class PayRepository extends BaseRepository
{
    public $instance;
    public $wxMini;
    public $log;
    protected $privateKey;
    protected $merchantPrivateKeyInstance;
    protected $platformPublicKeyInstance;

    public function __construct()
    {
        $this->log = new Logger('TestPay');
        $this->log->pushHandler(
            (new RotatingFileHandler(storage_path('/logs/TestPay.log'), 7, Logger::DEBUG, true, 0777))->setFormatter(new LineFormatter(null, null, true, true))
        );
        $this->wxMini = config('xh.wxMini');
        // 商户号
        $merchantId = $this->wxMini['merchantId'];
// 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://' . realpath($this->wxMini['merchantPrivateKeyFilePath']);
        $this->privateKey = $this->merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = $this->wxMini['serial'];

// 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $platformCertificateFilePath = 'file://' . realpath($this->wxMini['v3CertificateFilePath']);
        $this->platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

// 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

// 构造一个 APIv3 客户端实例
        $this->instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantCertificateSerial,
            'privateKey' => $this->privateKey,
            'certs' => [
                $platformCertificateSerial => $this->platformPublicKeyInstance,
            ],
        ]);
        parent::__construct();
    }

    public function mini($params)
    {
        $ordersno = $params['ordersno'] ?? '';
        $ModelMember = new MemberModel();
        $userId = $params['user']['id'] ?? 0;
        if (!isset($params['user']['mini_openid'])) {
            $member = $ModelMember->where(['id' => $userId])->select(['mini_openid'])->first();
            $openid = $member['mini_openid'] ?? '';
        } else {
            $openid = $params['user']['mini_openid'];
        }
        if (empty($openid)) {
            return $this->returnMsg(20, '会员信息异常,不能支付');
        }
        $ModelOrdersUser = new OrdersUserModel();
        $ModelOrders = new OrdersModel();
        $where = [];
        $where['where']['a.orderno'] = $ordersno;
//        $where['where']['a.xh_member_id'] = $userId;
        $fields = ['a.is_api_number', 'a.pay_money', 'a.sku_number',
            'b.id', 'b.ordersno', 'b.goods_name', 'b.goods_code'];
        $orderRow = $ModelOrdersUser->from($ModelOrdersUser->getTable() . ' AS a')
            ->leftJoin($ModelOrders->getTable() . ' AS b', 'a.orderno', '=', 'b.ordersno')
            ->select($fields)
            ->searchWhere($where)
            ->first();
        if (empty($orderRow)) {
            return $this->returnMsg(20, '指定的订单异常,不能支付');
        }
        $description = '商品编号:' . $orderRow['goods_code'] . ';号码:' . $orderRow['sku_number'];
        // 微信是分为单位的,所以要*100
        $money = floatval($orderRow['pay_money']) * 100;
        try {
            $resp = $this->instance
                ->chain('v3/pay/transactions/jsapi')
                ->post([
                    'json' => [
                        'mchid' => $this->wxMini['merchantId'],
                        'out_trade_no' => $ordersno,
                        'appid' => $this->wxMini['appid'],
                        'description' => $description,
                        'notify_url' => $this->wxMini['notifyUrl'],
                        'payer' => [
                            "openid" => $openid
                        ],
                        'amount' => [
                            'total' => $money,
                            'currency' => 'CNY'
                        ],
                    ],
                    'headers' => [
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json;charset=UTF-8',
                        'User-Agent' => request()->userAgent()
                    ]]);
            $statusCode = $resp->getStatusCode();
            $prepayId = null;
            if ($statusCode == 200) {
                if ($resp->getBody()->getSize() > 10) {
                    $body = $resp->getBody()->getContents();
                    $body = json_decode($body, true);
                    $prepayId = $body['prepay_id'] ?? '';
                }
            }
        } catch (\Exception $e) {
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                return $this->returnMsg(20, $r->getBody()->getContents());
            }
            return $this->returnMsg(20, '异常数据');
        }
        if (!empty($prepayId)) {
            $params = [
                'appId' => $this->wxMini['appid'],
                'timeStamp' => (string)Formatter::timestamp(),
                'nonceStr' => Formatter::nonce(),
                'package' => 'prepay_id=' . $prepayId,
            ];
            //签名数据
            $params += ['paySign' => Rsa::sign(
                Formatter::joinedByLineFeed(...array_values($params)),
                $this->privateKey
            ), 'signType' => 'RSA'];
            $params += [
                "provider" => "wxpay",
                "orderInfo" => []
            ];
        }
        return $this->returnMsg(200, '数据已获取' . $prepayId, $params);
    }

    //退款操作
    public function refunds($ordersno)
    {
//        $ordersno = $params['ordersno'] ?? '';
        if (empty($ordersno)) {
            return $this->returnMsg(20, '订单信息异常,不能操作');
        }
        $ModelOrders = new OrdersModel();
        $where = [
            'ordersno' => $ordersno
        ];
        $orderRow = $ModelOrders->where($where)->select(['id', 'status'])->first();
        if (empty($orderRow)) {
            return $this->returnMsg(20, '指定的订单异常,不能操作');
        }
        if (!in_array($orderRow['status'], [0, 1, 10, 11, 20, 30])) {
            return $this->returnMsg(20, '订单当前状态,不能操作退款');
        }
        // 支付信息
        $ModelPays = new PaysModel();
        $fields = ['id', 'transaction_id', 'money'];
        $where = [
            'ordersno' => $ordersno,
            'status' => 1
        ];
        $paysRow = $ModelPays->select($fields)->where($where)->first();
        if (empty($paysRow)) {
            return $this->returnMsg(20, '指定的订单没有支付信息,不能操作');
        }
        // 元转分
        $refundMoney = intval($paysRow['money'] * 100);

        try {
            $resp = $this->instance
                ->chain('v3/refund/domestic/refunds')
                ->post([
                    'json' => [
                        'transaction_id' => $paysRow['transaction_id'],
                        'out_refund_no' => $paysRow['transaction_id'],
                        'notify_url' => $this->wxMini['refundNotifyUrl'],
                        'amount' => [
                            'refund' => $refundMoney,
                            'total' => $refundMoney,
                            'currency' => 'CNY'
                        ],
                    ],
                    'headers' => [
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json;charset=UTF-8',
                        'User-Agent' => request()->userAgent()
                    ]]);
            $statusCode = $resp->getStatusCode();
            if ($statusCode != 200) {
                return $this->returnMsg(20, '提交退款返回异常:' . $statusCode);
            }
            if ($resp->getBody()->getSize() < 10) {
                return $this->returnMsg(20, '提交退款返回数据异常');
            }
            $body = $resp->getBody()->getContents();
            $body = json_decode($body, true);
            if (!isset($body['status'])) {
                return $this->returnMsg(20, '解析退款数据异常');
            }
            switch (strtoupper($body['status'])) {
                case 'SUCCESS':
                    $status = 24;
                    break;
                case 'CLOSED':
                    $status = 21;
                    break;
                case 'ABNORMAL':
                    $status = 23;
                    break;
                default:
                    $status = 22;
                    break;
            }
            //更新订单与原支付状态
            $ModelOrders->where(['id' => $orderRow['id']])->update(['status' => 52]);
            $ModelPays->where(['id' => $paysRow['id']])->update(['status' => $status, 'content' => json_encode($body)]);
            return $this->returnMsg(200, '退款申请已提交,等待支付系统完成退款');
        } catch (\Exception $e) {
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                return $this->returnMsg(20, $r->getBody()->getContents());
            }
            return $this->returnMsg(20, '异常数据');
        }
    }

    // 解析回调内容
    protected function notifyDecode($request)
    {
        // header信息
        $inWechatpaySignature = '';
        if ($request->hasHeader('Wechatpay-Signature')) {
            $inWechatpaySignature = $request->header('Wechatpay-Signature');
        }
        $inWechatpayTimestamp = '';
        if ($request->hasHeader('Wechatpay-Timestamp')) {
            $inWechatpayTimestamp = $request->header('Wechatpay-Timestamp');
        }
        $inWechatpaySerial = '';
        if ($request->hasHeader('Wechatpay-Serial')) {
            $inWechatpaySerial = $request->header('Wechatpay-Serial');
        }
        $inWechatpayNonce = '';
        if ($request->hasHeader('Wechatpay-Nonce')) {
            $inWechatpayNonce = $request->header('Wechatpay-Nonce');
        }
        $requestId = '';
        if ($request->hasHeader('Request-ID')) {
            $requestId = $request->header('Request-ID');
        }
        $this->log->info("inWechatpaySignature:" . $inWechatpaySignature . ';inWechatpayTimestamp:' . $inWechatpayTimestamp . ';inWechatpaySerial:' . $inWechatpaySerial . ';inWechatpayNonce:' . $inWechatpayNonce . ';requestId:' . $requestId);

        // 请根据实际情况获取，例如: file_get_contents('php://input');
        $inBody = file_get_contents("php://input");
        $this->log->info('微信回调:' . $inBody);
        // 在商户平台上设置的APIv3密钥
        $apiv3Key = $this->wxMini['apiv3Key'];
        // 检查通知时间偏移量，允许30分钟之内的偏移
        $timeOffsetStatus = 1800 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        $verifiedStatus = Rsa::verify(
        // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $this->platformPublicKeyInstance
        );
        if ($verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);
            // 使用PHP7的数据解构语法，从Array中解构并赋值变量
            ['resource' => [
                'ciphertext' => $ciphertext,
                'nonce' => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            $this->log->info('inBodyResource数据:' . $inBodyResource);
            // 把解密后的文本转换为PHP Array数组
            return (array)json_decode($inBodyResource, true);
        }
        return null;
    }

//    public function notifyTest($request)
//    {
//        $data = $this->notifyDecode($request);
//        return $this->returnMsg(200, '数据未获取', $data);
//    }

    public function notify($request)
    {
        $data = $this->notifyDecode($request);
        if (!empty($data) && is_array($data)) {
            $this->savePayLog($data);
            return $this->returnMsg(200, '数据已获取');
        }
        return $this->returnMsg(400, '数据未获取');
    }

    // 退款回调
    public function notifyRefunds($request)
    {
        $data = $this->notifyDecode($request);
        if (empty($data) || !is_array($data)) {
            $this->log->info('返回解密数据未获取' . serialize($data));
            return $this->returnMsg(400, '数据未获取');
        }
        $transactionId = $data['transaction_id'];
        $ModelPays = new PaysModel();
        $paysRow = $ModelPays->where(['transaction_id' => $transactionId])->select(['id', 'ordersno', 'status'])->first();
        if (empty($paysRow)) {
            $this->log->info('退款数据未获取' . serialize($paysRow));
            return $this->returnMsg(4, '退款数据未获取');
        }
        if ($paysRow['status'] != 22) {
            $this->log->info('退款数据状态异常' . $paysRow['status']);
            return $this->returnMsg(4, '退款数据状态异常');
        }
        $ModelPays->where(['id' => $paysRow['id']])->update(['status' => 24, 'content' => json_encode($data)]);
        $ModelOrders = new OrdersModel();
        $where = [
            'ordersno' => $paysRow['ordersno'],
            'status' => 52
        ];
        $ModelOrders->where($where)->update(['status' => 53]);
        return $this->returnMsg(200, '数据已获取');
    }

    // 记录支付信息
    protected function savePayLog($data)
    {
        $money = $data['amount']['total'] ?? 0;
        $status = 1;
        if (strtoupper($data['trade_state']) != 'SUCCESS') {
            $status = 0;
        }
        $saveData = [
            "ordersno" => $data['out_trade_no'] ?? '',
            "out_trade_no" => $data['out_trade_no'] ?? '',
            "transaction_id" => $data['transaction_id'] ?? '',
            "content" => json_encode($data),
            "type" => 0,
            "created_at" => date('Y-m-d H:i:s'),
            //金额为分转元
            "money" => $money / 100,
            "status" => $status
        ];
        if (empty($saveData['transaction_id'])) {
            $this->log->info('返回的支付内容异常' . serialize($data));
            return $this->returnMsg(1, '返回的支付内容异常');
        }
        $Repository = new PaysRepository();
        $res = $Repository->add($saveData);
        if (!$res['code'] == 200) {
            return $res;
        }
        // 更新订单状态
        $ModelOrders = new OrdersModel();
        $ModelOrders->where(['ordersno' => $saveData['ordersno'], 'status' => 2])->update(['status' => 0]);
        return $this->returnMsg(200, '支付内容保存成功');
    }

    public function notifyTest($request)
    {
        // header信息
        $inWechatpaySignature = 'oHNB0rW7rLfZ0QkAB6Joa2L1mVshkUmN9zfl8sZY4ruNiQmgSOexiu0Zf3sm474izeM1irnw1nz3p34fi/tyUk66QZCumJKiY6QXhHSfEkncciDdPGMOk0M8CT9z6TQF5HOUrTcJX2K5pafILA+vPih9ot9RhnzbvQhCqWDgA1eBkRAehENCohAJGvAvtBe0UUP58mNcgr0yjVOftrBHcz9qoJgmX2cqwzm2+RvBE44vyjDGtR8DGEsxdARYF4y4wir4OdhgtwRvrVsxxAJbibNuY+JFyieQ5Q930ektjkTboQoAdTy+2iLgWpR/Cs8jHH3T0CPaG8Rr0Hs6xKNGqQ==';
        $inWechatpayTimestamp = '1709366078';
        $inWechatpaySerial = '5FC9AF8D41AB3D04B27C03A21B5090750A764376';
        $inWechatpayNonce = '4And3Ea8UPlKBsK6NfVdum99vgy1D4Sk';
        $requestId = '';

        // 请根据实际情况获取，例如: file_get_contents('php://input');
        $inBody = '{"id":"6a8aae32-61e4-5989-b107-fe27c5e7d140","create_time":"2024-03-02T15:54:38+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"DYasNosVnnIFrLz3KmPBebSL92nw2tIjCsjqd2V6ZMO2yURrtewjk02xqMHq8Y2r+vbC3HjVlp+11QnDUAxoCKLR5fJKwIf0A27HKjEdIGRnGnXZeVuw6NN4lwJsyJw4pXtfOf2X+yuTIfzVjja2gQgw9Ddz+ccfK/uzUra90uUvg8yim3sOwe5pUleO/8qkzsROgUDsMfqKMOoRPmYkfSgA/wqOEh56+TcS7dkFgnaYCDSM+0/9I3IvuUN+Gn/tyBea9geTB+TP1dICjSACxN+3EYU4z+dJDPh/npazr4R2WVlVqFIHx3UilE1qEeHa+EDsHvnuiknu/aDmvYq7O0fDeBaAvU1hkBUn4zM7lTWZlmyD0TYM+nMwsj1PFzfW/ooXHy+sNxERXFTvUqHxCRpxWoHzmX7kdmUflYdqzcibIIxF68Wb6GDxcIUSJ2wEbvlNJzml8HAAEb6zFn6m+5xKrJXs+0afZs1fvmiCVzH1tV5tOESIVW+sSKBAqK/PcjyZ+W/B/X1Jo4Cuh5lDBaF+Kd/ZbiRtICO8Yy7f8TEKRcIAITeaCceOBjIAqDVxdb63Yhw=","associated_data":"transaction","nonce":"eYq5ZNwN1dbx"}}';

        // 在商户平台上设置的APIv3密钥
        $apiv3Key = $this->wxMini['apiv3Key'];
//        return $this->returnMsg(20, '数据已获取:' . $this->platformPublicKeyInstance);
        // 检查通知时间偏移量，允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        $verifiedStatus = Rsa::verify(
        // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $this->platformPublicKeyInstance
        );
        if ($verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);

            // 使用PHP7的数据解构语法，从Array中解构并赋值变量
            ['resource' => [
                'ciphertext' => $ciphertext,
                'nonce' => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);
            // print_r($inBodyResourceArray);// 打印解密后的结果
//            $this->wxPayLog($inBodyResourceArray);
            return $this->returnMsg(200, '数据已获取', $inBodyResourceArray);
        }
        return $this->returnMsg(400, '数据未获取');
    }
}
